/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2007 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or ( at your option ) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef _CLASS_TZ_MANAGER_
#define _CLASS_TZ_MANAGER_

#include "m_system_cpp.h"

#define TZREG "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
#define TZREG_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"

/**
 * This structure is used by GetTimeZoneInformationByIndex to retrieve
 * timezone information from windows' registry
 **/
typedef struct _REG_TIME_ZONE_INFORMATION 
{
		LONG Bias;
		LONG StandardBias;
		LONG DaylightBias;
		SYSTEMTIME StandardDate;
		SYSTEMTIME DaylightDate;
}
REG_TIME_ZONE_INFORMATION, *PREG_TIME_ZONE_INFORMATION;

struct TZ_MAP 
{
  DWORD Index;
  LPTSTR Name;
};

/**
 * This structure is an element of the CTzManager.
 * It holds information about a timezone, which are required
 * to display the correct time of a contact which is not
 * in the same timezone as the owner contact.
 **/
struct CTimeZone : public REG_TIME_ZONE_INFORMATION
{
	LPTSTR	pszName;
	LPTSTR	pszDisplay;
	LPTSTR	pszDLT;
	LPTSTR	pszSTD;
	DWORD	dwIndex;		//old, only supportet in win9x

	/**
	 * This is the default constructure, which resets
	 * all attributes to NULL.
	 **/
	CTimeZone();

	/**
	 * The default construcor's task ist to clear out
	 * all pieces of the used memory.
	 **/
	~CTimeZone();

	/**
	 * This method can be used to basically convert a Windows
	 * timezone to the format, known by miranda.
	 *
	 * @warning	This operation does not work vice versa in
	 *					all cases, as there are sometimes more then
	 *					one Windows timezones with the same Bias.
	 **/
	BYTE ToMirandaTimezone() const;

	/**
	 * This operator translates the content of this object to
	 * a TIME_ZONE_INFORMATION structure as it is required by 
	 * several windows functions.
	 **/
	operator TIME_ZONE_INFORMATION() const;
};

/**
 * This class sorts the data by bias and caption
 **/
class CTzByBias : public LIST<CTimeZone>
{
	static INT	sortFunc( const CTimeZone *tz1, const CTimeZone *tz2 );

public:
	CTzByBias();
	~CTzByBias();

	inline CTimeZone*	find	( CTimeZone* p ) const { return ( CTimeZone* )li.List_Find(( SortedList* )this, p ); }
	CTimeZone*			find	( LONG Bias, LPTSTR pszDisplay ) const;
};

/**
 * This class sorts the data by index and caption
 **/
class CTzByIndex : public LIST<CTimeZone>
{
	static INT	sortFunc( const CTimeZone *tz1, const CTimeZone *tz2 );

public:
	CTzByIndex();
	~CTzByIndex();

	VOID destroy( VOID );

	inline CTimeZone*	find	( CTimeZone* p ) const { return ( CTimeZone* )li.List_Find(( SortedList* )this, p ); }
	CTimeZone*			find	( DWORD idx ) const;
};

/**
 * This class sorts the data by name and caption
 **/
class CTzByName : public LIST<CTimeZone>
{
	static INT	sortFunc( const CTimeZone *tz1, const CTimeZone *tz2 );

public:
	CTzByName();
	~CTzByName();

	VOID destroy( VOID );

	inline CTimeZone*	find	( CTimeZone* p ) const { return ( CTimeZone* )li.List_Find(( SortedList* )this, p ); }
	CTimeZone*			find	( LPTSTR pszName ) const;
};

/**
 * This class is a deriviant of miranda's SortedList and holds all timezones
 * known by Windows. By default there is no API to list timezones, so we
 * need to get the information directly from the registry. In order to avoid
 * heavy reading operations from registry, this class has the task to cache
 * all required information for much faster access.
 **/
class CTzManager
{
	CTzByBias		_bias;
	CTzByIndex		_index;
	CTzByName		_name;

public:

	/**
	 * This is a constant public reference of the list of all timezones,
	 * known by windows sorted by bias.
	 * It uses binary search to find items as fast as possible.
	 **/
	const CTzByBias&	Bias;

	/**
	 * This is a constant public reference of the list of all timezones,
	 * known by windows sorted by timezone index.
	 * It uses binary search to find items as fast as possible.
	 **/
	const CTzByIndex&	Index;

	/**
	 * This is a constant public reference of the list of all timezones,
	 * known by windows sorted by timezone name.
	 * It uses binary search to find items as fast as possible.
	 **/
	const CTzByName&	Name;

	CTzManager();

	/**
	 * This method loads all information about timezones from windows' registry.
	 **/
	INT					GetTimeZonesFromRegistry			();

	/**
	 * This method trys to find some default windows timezone idices for a given
	 * miranda timezone.
	 *
	 * @param		MirTz		- this is a miranda timezone with values between -24 and 24.
	 *
	 * @return		This method returns a @TZ_MAP struct of a windows timezone, which is maps
	 *				the @MirTz value,name or {-1,NULL} if no windows timezone index exists.
	 **/
	TZ_MAP				IndexFromMirandaTimeZone			( const CHAR MirTz ) const;

	/**
	 * This method trys to find the index for the contact's windows timezone. If no index is 
	 * available for the contact, the @e dwIndex value retrieves the bias of miranda's timezone value.
	 *
	 * @warning	Make sure you convert @e dwIndex to CHAR if the function returns 1 in order to get
	 * the correct miranda timezone!
	 *
	 * @param		hContact	- the HANDLE of the contact to read timezone information for
	 * @param		szProto		- contact's protocol
	 * @param		dwIndex		- This reference to an DWORD retrieves the index of the windows timezone,
	 *							  set for a contact, if the function returns 0. If the index can't be 
	 *							  determined, the variable retrieves the value for a miranda's 'timezone' setting
	 *							  or -100 (unspecified) and the function returns 1.
	 * @param		pszName		- This reference to an LPTSTR retrieves the name of the windows timezone,
	 *
	 * @retval		tz			- The index for a windows timezone was found for the contact.
	 * @retval		NULL		- There is no index, but if the contact's 'timezone' setting is valid, @e dwIndex retrieved its value.
	 *							  If not, dwIndex is -100 (unspecified).
	 **/
	CTimeZone*			GetContactTimeZone				( HANDLE hContact, LPCSTR szProto, DWORD& dwIndex, LPTSTR &pszName ) const;

	/**
	 * This method trys to find the index for the contact's windows timezone. If no index is 
	 * available for the contact, the @e dwIndex value retrieves the bias of miranda's timezone value.
	 *
	 * @warning	Make sure you convert @e dwIndex to CHAR if the function returns 1 in order to get
	 * the correct miranda timezone!
	 *
	 * @param		hContact	- the HANDLE of the contact to read timezone information for
	 * @param		szProto		- contact's protocol
	 * @param		dwIndex		- This reference to an DWORD retrieves the index of the windows timezone,
	 *							  set for a contact, if the function returns 0. If the index can't be 
	 *							  determined, the variable retrieves the value for a miranda's 'timezone' setting
	 *							  or -100 (unspecified) and the function returns 1.
	 * @param		pszName		- This reference to an LPTSTR retrieves the name of the windows timezone,
	 *
	 * @retval	CTRLF_... flag	- The index for a windows timezone was found for the contact.
	 * @retval	0				- There is no index, but if the contact's 'timezone' setting is valid,
	 *														@e dwIndex retrieved its value.	If not, dwIndex is -100 (unspecified).
	 **/
	WORD				GetContactTimeZoneIndexCtrl		( HANDLE hContact, LPCSTR szProto, DWORD& dwIndex, LPTSTR &pszName ) const;

	/**
	 * This method looks up the contact's database settings for information about his timezone.
	 * If it is somehow possible, the method writes all available timezone information into the
	 * @e tzi structure
	 *
	 * @param		hContact	- the HANDLE of the contact to read timezone information for
	 * @param		szProto		- contact's protocol
	 * @param		tzi				- pointer to a TIME_ZONE_INFORMATION structure, which will retrieve the information
	 *
	 * @retval	0		- the timezone was found and successfully written to @e tzi
	 * @retval	1		- there are no timezone information available for the contact
	 **/
	INT					GetContactTimeZoneInformation	( HANDLE hContact, LPCSTR szProto, TIME_ZONE_INFORMATION& tzi ) const;
};

#endif /* _CLASS_TZ_MANAGER_ */